/**
 *  @file DateTime.cpp
 */
#if defined(WIN32)
#   include <windows.h>
#elif defined(linux)
#   include <time.h>
#endif
#include <math.h>
#include "DateTime.h"
#include "TimeDuration.h"
#include "../lang/IntCheck.h"
#include "../exceptions/ArgumentException.h"
#include "../exceptions/OverflowException.h"
#include "../text/CFString.h"
#include "../common/UtilFuncT.h"

using namespace cppflib::lang;

namespace cppflib
{

namespace util
{

#define MIN_YEAR 1
#define MAX_YEAR 9999
#define MIN_MONTH 1
#define MAX_MONTH 12
#define MIN_DAY 1

#define MIN_HOUR 0
#define MAX_HOUR 23
#define MIN_MIN 0
#define MAX_MIN 59
#define MIN_SEC 0
#define MAX_SEC 59
#define MIN_MILLISEC 0
#define MAX_MILLISEC 999

//! 0001 Jan 1, 00:00:00
PRIVATE const i64_t MIN_DATETIMEVALUE = 0;  
//! 9999 Dec 31, 23:59:59 999msec 9999ticks 
PRIVATE const i64_t MAX_DATETIMEVALUE = I64N(3155378975999999999);

using namespace cppflib::exceptions;

/**
 *  Check a valid date representable by this DateTime
 */
PRIVATE bool __CheckDatePart(int year, int month, int day)
{
   // check year 
   if (year < MIN_YEAR || year > MAX_YEAR)
      throw ArgumentException(_S("Year must be between ") NUM2STR(MIN_YEAR) _S(" and ") NUM2STR(MAX_YEAR), -1);

   // check month
   if (month < MIN_MONTH || month > MAX_MONTH)
      throw ArgumentException(_S("Month must be between ") NUM2STR(MIN_MONTH) _S(" and ") NUM2STR(MAX_MONTH), -1);

   // check day
   int nrDaysInMonth = DateTime::DaysInMonth(year, month);
   if (day < MIN_DAY || day > nrDaysInMonth)
      throw ArgumentException(_S("Number of days in this month is out of range"), -1);

   return true;
}


/**
 *   Check a valid tiime representable by this DateTime
 */
PRIVATE bool __CheckTimeOfDayPart(int hour, int min, int sec, int milliSec)
{

   // check hour
   if (hour < MIN_HOUR || hour > MAX_HOUR)
      throw ArgumentException(_S("Hour must be between ") NUM2STR(MIN_HOUR) _S(" and ") NUM2STR(MAX_HOUR), -1);

   // check minute
   if (min < MIN_MIN || min > MAX_MIN)
      throw ArgumentException(_S("Minute must be between ") NUM2STR(MIN_MIN) _S(" and ") NUM2STR(MAX_MIN), -1);

   // check sec
   if (sec < MIN_SEC || sec > MAX_SEC)
      throw ArgumentException(_S("Second must be between ") NUM2STR(MIN_SEC) _S(" and ") NUM2STR(MAX_SEC), -1);

   // check millisec
   if (milliSec < MIN_MILLISEC || milliSec > MAX_MILLISEC)
      throw ArgumentException(_S("Millisecond must be between ") NUM2STR(MIN_MILLISEC) _S(" and ") NUM2STR(MAX_MILLISEC), -1);

   return true;
}

/**
 *  Check both date and time
 */
PRIVATE bool __CheckDateAndTime(int year, int month, int day,
                                int hour, int min, int sec, int milliSec)
{
   __CheckDatePart(year, month, day);
   __CheckTimeOfDayPart(hour, min, sec, milliSec);
   return true;
}

/**
 *  To count number of days since year 0001
 */
PRIVATE int __CountDaysByYear(int year)
{
   year -= 1;
   return (year * 365) + (year / 4 - year / 100 + year / 400);
}

/**
 *  To count number of days until 'month' since the start of this year
 */
PRIVATE int __CountDaysByMonth(int year, int month)
{
   int nrDays = 0;
   for (int i = 1; i < month; ++i)
      nrDays += DateTime::DaysInMonth(year, i);
   return nrDays;
}

/**
 *  Get local datetime
 */
PRIVATE DateTime __GetNow()
{
#if defined(WIN32)
   SYSTEMTIME localTime;
   ::GetLocalTime(&localTime);
   return DateTime(localTime.wYear, localTime.wMonth, localTime.wDay, 
                   localTime.wHour, localTime.wMinute, localTime.wSecond, 
                   localTime.wMilliseconds, DateTime::DTK_LOCAL);
#elif defined(linux)
   struct tm timeStruct;
   time_t t = time(NULL);
   localtime_r(&t, &timeStruct);
   return DateTime(timeStruct.tm_year + 1900, timeStruct.tm_mon + 1, timeStruct.tm_mday, 
                   timeStruct.tm_hour, timeStruct.tm_min, timeStruct.tm_sec, DateTime::DTK_LOCAL);
#endif
}

/**
 *  Get UTC datetime
 */
PRIVATE DateTime __GetUtcNow()
{
#if defined(WIN32)
   SYSTEMTIME utcTime;
   ::GetSystemTime(&utcTime);
   return DateTime(utcTime.wYear, utcTime.wMonth, utcTime.wDay, 
                   utcTime.wHour, utcTime.wMinute, utcTime.wSecond, 
                   utcTime.wMilliseconds, DateTime::DTK_UTC);
#elif defined(linux)
   struct tm timeStruct;
   time_t t = time(NULL);
   gmtime_r(&t, &timeStruct);
   return DateTime(timeStruct.tm_year + 1900, timeStruct.tm_mon + 1, timeStruct.tm_mday, 
                   timeStruct.tm_hour, timeStruct.tm_min, timeStruct.tm_sec, DateTime::DTK_UTC);
#endif
}

/**
 *  Convert the human readable datetime to number of ticks
 */
void DateTime::InitDateTime(int year, int month, int day, 
                            int hour, int min, int sec, int milliSec, 
                            datetimekind_t dtKind)
{
   // do checking before assignment
   try {
      __CheckDateAndTime(year, month, day, hour, min, sec, milliSec);
   }
   catch (ArgumentException &exp) {
      THROW_CTOR_EXCEP_CODE(ArgumentException, exp.GetMsg(), exp.GetErrCode());  // throw again
   }
   
   this->ticks = __CountDaysByYear(year) * TimeDuration::TICKS_PERDAY;
   this->ticks += (__CountDaysByMonth(year, month) * TimeDuration::TICKS_PERDAY);
   this->ticks += ((day - 1) * TimeDuration::TICKS_PERDAY);
   this->ticks += (hour * TimeDuration::TICKS_PERHOUR);
   this->ticks += (min * TimeDuration::TICKS_PERMIN);
   this->ticks += (sec * TimeDuration::TICKS_PERSEC);
   this->ticks += (milliSec * TimeDuration::TICKS_PERMILLISEC);
   this->dtKind = dtKind;
}

DateTime::DateTime(const DateTime& dt) : BaseObject(dt)
{
   this->ticks = dt.ticks;
   this->dtKind = dt.dtKind;
}

const DateTime& DateTime::operator=(const DateTime& dt)
{
   BaseObject::operator=(dt);
   this->ticks = dt.ticks;
   this->dtKind = dt.dtKind;
   return *this;
}

DateTime::DateTime(void)
{
   InitDateTime(1, 1, 1, 0, 0, 0, 0, DateTime::DTK_UTC);
}

DateTime::DateTime(i64_t ticks)
{
   if (ticks < MIN_DATETIMEVALUE || ticks > MAX_DATETIMEVALUE) {
      CFString s;
      s.Format(_S("ticks must be between %lld and %lld"), MIN_DATETIMEVALUE, MAX_DATETIMEVALUE);
      THROW_CTOR_EXCEP(ArgumentException, s);
   }
   this->ticks = ticks;
   this->dtKind = DateTime::DTK_UTC;
}

DateTime::DateTime(i64_t ticks, datetimekind_t dtKind)
{
   if (ticks < MIN_DATETIMEVALUE || ticks > MAX_DATETIMEVALUE) {
      CFString s;
      s.Format(_S("ticks must be between %lld and %lld"), MIN_DATETIMEVALUE, MAX_DATETIMEVALUE);
      THROW_CTOR_EXCEP(ArgumentException, s);
   }
   this->ticks = ticks;
   this->dtKind = dtKind;
}

DateTime::DateTime(int year, int month, int day)
{
   InitDateTime(year, month, day, 0, 0, 0, 0, DateTime::DTK_UTC);
}

DateTime::DateTime(int year, int month, int day, datetimekind_t dtKind)
{
   InitDateTime(year, month, day, 0, 0, 0, 0, dtKind);
}

DateTime::DateTime(int year, int month, int day, int hour, int min, int sec)
{
   InitDateTime(year, month, day, hour, min, sec, 0, DateTime::DTK_UTC);
}

DateTime::DateTime(int year, int month, int day, int hour, int min, int sec, datetimekind_t dtKind)
{
   InitDateTime(year, month, day, hour, min, sec, 0, dtKind);
}

DateTime::DateTime(int year, int month, int day, int hour, int min, int sec, int milliSec)
{
   InitDateTime(year, month, day, hour, min, sec, milliSec, DateTime::DTK_UTC);
}

DateTime::DateTime(int year, int month, int day, int hour, int min, int sec, int milliSec, datetimekind_t dtKind)
{
   InitDateTime(year, month, day, hour, min, sec, milliSec, dtKind);
}


DateTime::~DateTime(void)
{
   ReleaseBeforeThrowing();
}

void DateTime::ReleaseBeforeThrowing()
{
   // nothing to release in DateTime
}

int DateTime::Year() const
{
   const int YEAR400_DAYS = 146097;   // num of days in 400 years (4 hundred years + 1 leap year)
   const int YEAR100_DAYS = 36524;    // num of days in 100 years (25 four years - 1 leap year)
   const int YEAR4_DAYS = 1461;       // num of days in 4 years (3 leap + 1 normal)
   
   int y = 1;  // year starts from 1   

   do {
       int t = static_cast<int>(ticks / TimeDuration::TICKS_PERDAY);

       // num of 400 years
       int nr400Years = t / YEAR400_DAYS;
       t = t % YEAR400_DAYS;
       if (nr400Years > 0 && t == 0) {
          y += (nr400Years * 400);
          break;
       }

       // num of 100 years
       int nr100Years = t / YEAR100_DAYS;
       t = t % YEAR100_DAYS;
       if (nr100Years > 0 && t == 0) {
          y += (nr400Years * 400 + nr100Years * 100);
          break;
       }

       // num of 4 years
       int nr4Years = t / YEAR4_DAYS;
       t = t % YEAR4_DAYS;
       if (nr4Years > 0 && t == 0) {
          y += (nr400Years * 400 + nr100Years * 100 + nr4Years * 4);
          break;
       }
       
       // num of 1 years (at most 3 since the 4th year should have 366 days)
       int nr1Years = t / 365;
       nr1Years = (nr1Years >= 4) ? 3 : nr1Years; // should be at most 3 years
       y += (nr400Years * 400 + nr100Years * 100 + nr4Years * 4 + nr1Years);
   } while(false);

   return y;

/*
   // this one is correct but quite time consuming

   int t = static_cast<int>(ticks / TimeDuration::TICKS_PERDAY);
   int y = 1;
   while (true) {
      t -= DateTime::IsLeapYear(y) ? 366 : 365;
      if (t < 0) // t == 0 is next year
         break;
      ++y;
   }
   
   return y;
*/
}


/**
 *  A function to get month or day in a month
 *
 *  @param [in] isGetMonth -- true to get month; false to get day
 *  @param [in] nrTicks -- number of ticks
 *  @param [in] year -- year of the 'ticks'
 */
PRIVATE int __GetMonthOrDay(bool isGetMonth, i64_t nrTicks, int year)
{
   int nrDays = static_cast<int>(nrTicks / TimeDuration::TICKS_PERDAY);
   int month = 1;

   nrDays -= __CountDaysByYear(year);  // days left in this year
   while (nrDays > 0) {
      int tDays = DateTime::DaysInMonth(year, month);
      if (nrDays >= tDays)
         nrDays -= tDays;
      else
         break;

      ++month;
   }

   if (isGetMonth)  
      return month;  // return month

   nrDays += 1; // every day of a month starts from 1, not 0
   return nrDays;  // return day
}

int DateTime::Month() const
{
   return __GetMonthOrDay(true, ticks, Year());
}

int DateTime::Day() const
{
   return __GetMonthOrDay(false, ticks, Year());
}

int DateTime::Hour() const
{
   i64_t t = ticks / TimeDuration::TICKS_PERHOUR;
   return static_cast<int>(t % 24);
}

int DateTime::Min() const
{
   i64_t t = ticks / TimeDuration::TICKS_PERMIN;
   return static_cast<int>(t % 60);
}

int DateTime::Sec() const
{
   i64_t t = ticks / TimeDuration::TICKS_PERSEC;
   return static_cast<int>(t % 60);
}

int DateTime::MilliSec() const
{
   i64_t t = ticks / TimeDuration::TICKS_PERMILLISEC;
   return static_cast<int>(t % 1000);
}

i64_t DateTime::Ticks() const
{
   return ticks;
}

DateTime::datetimekind_t DateTime::Kind() const
{
   return dtKind;
}

bool DateTime::IsLeapYear() const
{
   return DateTime::IsLeapYear(Year());
}

/**
 *  Add a time duration
 */
DateTime& DateTime::Add(const TimeDuration &td)
{
   return AddTicks(td.TotalTicks());
}

/**
 *  Add years which can be +ve or -ve
 *
 *  @note month and time part are not affected
 *        day of month may be changed according to leap years
 */
DateTime& DateTime::AddYears(int years)
{
   int thisYear = Year();
   if (years < 0) {
      if (thisYear <= (-years))
         throw OverflowException(_S("Year is less than ") NUM2STR(MIN_YEAR), -1);
   }
   else {
      if (years >= MAX_YEAR || thisYear + years > MAX_YEAR)
         throw OverflowException(_S("Year exceeds ") NUM2STR(MAX_YEAR), -1);
   }
   
   int targetYear = thisYear + years;
   int diffDays = __CountDaysByYear(targetYear) - __CountDaysByYear(thisYear);
   int oldDay = Day();

   // update
   this->AddTicks(diffDays * TimeDuration::TICKS_PERDAY);

   // adjustment since the 'day' part should not be changed if possible
   int newDay = Day();
   if (oldDay != newDay) {
      if (newDay - oldDay == 1)
         this->AddTicks(-1 * TimeDuration::TICKS_PERDAY);  // 1 day ahead
      else if (newDay - oldDay == -1)
         this->AddTicks(1 * TimeDuration::TICKS_PERDAY);   // 1 day lag
      else if (newDay > oldDay)
         this->AddTicks(1 * TimeDuration::TICKS_PERDAY);   // e.g. newDay == 29, oldDay == 1
      else if (newDay < oldDay)
         this->AddTicks(-1 * TimeDuration::TICKS_PERDAY);  // e.g. newDay == 1, oldDay == 29
   }
   
   return *this;
}

/**
 *  Shift one month forward (future) or backward (past)
 *
 *  @param [in] isShiftForward -- true means shift to future
 *  @param [in] dayOfMonth -- target day of month
 */
void DateTime::ShiftOneMonth(bool isShiftForward, int dayOfMonth)
{
   if (isShiftForward) {
      // forward one month
      int y = Year();
      int m = Month();
      int daysLeftInThisMonth = DateTime::DaysInMonth(y, m) - Day();

      // days in next month
      m += 1;
      if (m > 12) {
         y += 1;
         m = 1;
      }
      int daysInNextMonth = DateTime::DaysInMonth(y, m);
      daysInNextMonth = cppflib::Min<int>(daysInNextMonth, dayOfMonth);
      
      this->AddDays(daysLeftInThisMonth + daysInNextMonth);
   }
   else {
      // go back one month
      int y = Year();
      int m = Month();
      int daysLeftInThisMonth = Day();

      // days in previous month
      m -= 1;
      if (m < 1) {
         y -= 1;
         m = 12;
      }

      int daysInPrevMonth = DateTime::DaysInMonth(y, m);
      daysInPrevMonth = (daysInPrevMonth > dayOfMonth) ? (daysInPrevMonth - dayOfMonth) : 0;
      
      this->AddDays(-(daysLeftInThisMonth + daysInPrevMonth));
   }
}

/**
 *  Add months. Day of month is adjusted to the last day of target month if necessary
 *
 *  @param [in] months -- number of months to add (+ve or -ve)
 */
DateTime& DateTime::AddMonths(int months)
{
   this->AddYears((months / 12)); // add years first if any
   months = months % 12; // remaining months
   bool isPos = true;

   if (months < 0) {
      isPos = false;
      months = -months;
   }

   int dayOfMonth = Day();
   for (int i = 0; i < months; ++i) {
      ShiftOneMonth(isPos, dayOfMonth);
   }

   return *this;
}

/**
 *  Add days
 */
DateTime& DateTime::AddDays(int days)
{
   if (CheckOverflow(lang::AOP_MUL, (i64_t)days, TimeDuration::TICKS_PERDAY)) {
      throw ArgumentException(_S("Argument value too big or too small"), -1);
   }

   return AddTicks(days * TimeDuration::TICKS_PERDAY);
}

/**
 *  Add hours
 */
DateTime& DateTime::AddHours(int hours)
{
   if (CheckOverflow(lang::AOP_MUL, (i64_t)hours, TimeDuration::TICKS_PERHOUR)) {
      throw ArgumentException(_S("Argument value too big or too small"), -1);
   }

   return AddTicks(hours * TimeDuration::TICKS_PERHOUR);
}

/**
 *  Add minutes
 */
DateTime& DateTime::AddMinutes(i64_t mins)
{
   if (CheckOverflow(lang::AOP_MUL, mins, TimeDuration::TICKS_PERMIN)) {
      throw ArgumentException(_S("Argument value too big or too small"), -1);
   }

   return AddTicks(mins * TimeDuration::TICKS_PERMIN);
}

/**
 *  Add seconds
 */
DateTime& DateTime::AddSeconds(i64_t seconds)
{
   if (CheckOverflow(lang::AOP_MUL, seconds, TimeDuration::TICKS_PERSEC)) {
      throw ArgumentException(_S("Argument value too big or too small"), -1);
   }

   return AddTicks(seconds * TimeDuration::TICKS_PERSEC);
}

/**
 *  Add milliseconds
 */
DateTime& DateTime::AddMilliSeconds(i64_t milliSecs)
{
   if (CheckOverflow(lang::AOP_MUL, milliSecs, TimeDuration::TICKS_PERMILLISEC)) {
      throw ArgumentException(_S("Argument value too big or too small"), -1);
   }

   return AddTicks(milliSecs * TimeDuration::TICKS_PERMILLISEC);
}

/**
 *  Add ticks 
 *
 *  @param [in] ticks -- +ve or -ve
 */
DateTime& DateTime::AddTicks(i64_t ticks)
{
   if (CheckOverflow(lang::AOP_ADD, this->ticks, ticks)) {
      throw OverflowException(_S("DateTime exceeds valid range"), -1);
   }

   i64_t tempTicks = this->ticks + ticks;
   if (tempTicks <= MIN_DATETIMEVALUE || tempTicks > MAX_DATETIMEVALUE) {
      throw OverflowException(_S("DateTime exceeds valid range"), -1);
   }

   this->ticks = tempTicks;
   return *this;
}

/**
 *  Compare this datetime with another
 *
 *  @return 0 -- same 
 *  @return -1 -- this datetime is earlier
 *  @return 1 -- this datetime is later
 */
int DateTime::Compare(const DateTime& dt) const
{
   DateTime thisUtc = ToUtcTime();
   DateTime dtUtc = dt.ToUtcTime();

   if (thisUtc.Ticks() > dtUtc.Ticks())
      return 1;
   else if (thisUtc.Ticks() < dtUtc.Ticks())
      return -1;

   return 0;
}

/**
 *  Convert current datetime to local datetime
 */
DateTime DateTime::ToLocalTime() const
{
   if (this->dtKind == DateTime::DTK_LOCAL)
      return *this;

   i64_t diffTicks = __GetNow().Ticks() - __GetUtcNow().Ticks();
   return DateTime(this->ticks + diffTicks, DateTime::DTK_LOCAL);
}


/**
 *  Convert current datetime to Utc datetime
 */
DateTime DateTime::ToUtcTime() const
{
   if (this->dtKind == DateTime::DTK_UTC)
      return *this;

   i64_t diffTicks = __GetUtcNow().Ticks() - __GetNow().Ticks();
   return DateTime(this->ticks + diffTicks, DateTime::DTK_UTC);
}

/**
 * Get the date part of this instance
 */
DateTime DateTime::Date()
{
   return DateTime(Year(), Month(), Day(), this->dtKind);
}

/**
 * Get the elapsed time since today's midnight 00:00:00
 */
TimeDuration DateTime::TimeOfDay() const
{
   i64_t t = ticks % TimeDuration::TICKS_PERDAY;
   return TimeDuration(t);
}

/**
 *  Compute day of week
 */
cppflib::util::dayofweek_t DateTime::DayOfWeek()
{
   return DateTime::DayOfWeek(Year(), Month(), Day());
}

/**
 *  Get the day of this year 
 *
 *  @return 1 - 366
 */
int DateTime::DayOfYear() const
{
   int nrDays = static_cast<int>(ticks / TimeDuration::TICKS_PERDAY);
   nrDays -= __CountDaysByYear(Year());  // days left in this year
   return nrDays + 1;  // since we start from 1
}

bool DateTime::Equals(const BaseObject &obj) const
{
   if (!this->EqualType(obj))
       return false;

   const DateTime *pDt = static_cast<const DateTime*>(&obj);

   // it is a bit different here, only true if and only if 
   // both ticks and datetime kind are the same without conversion
   if (this->ticks == pDt->Ticks() && this->dtKind == pDt->Kind())
      return true;

   return false;
}

u32_t DateTime::GetHash(void) const
{
   u32_t h = 0;
   int tickSize = sizeof(ticks);
   const u8_t * pData = reinterpret_cast<const u8_t*>(&ticks);

   // ticks
   for (int i = 0 ; i < tickSize; ++i) {
      u32_t htemp = static_cast<u32_t>((*pData++) & 0xff) << ((8 * i) % 32);
      h ^= htemp;
   }

   // datetime kind
   h ^= ((this->dtKind == DateTime::DTK_UTC) ? 0xff : 0xff00);

   return h;
}

pcwstr_t DateTime::ToString(void) const
{
   CFString *pStr = new CFString();
   pStr->Format(_S("%04d-%02d-%02d %02d:%02d:%02d"), Year(), Month(), Day(), Hour(), Min(), Sec());
   return static_cast<pcwstr_t>(*pStr);
}


bool DateTime::IsLeapYear(int year)
{
   // check year 
   if (year < MIN_YEAR || year > MAX_YEAR)
      throw ArgumentException(_S("Year must be between ") NUM2STR(MIN_YEAR) _S(" and ") NUM2STR(MAX_YEAR), -1);

   bool isLeapYear = false;

   if (year % 4 == 0) {
      if (year % 100 == 0) {
         if (year % 400 == 0) {
            isLeapYear = true;
         }
      }
      else {
         isLeapYear = true;
      }
   }

   return isLeapYear;
}

/**
 *  Number of days in a month
 */
int DateTime::DaysInMonth(int year, int month)
{
   if (month < MIN_MONTH || month > MAX_MONTH)
      throw ArgumentException(_S("Month must be between ") NUM2STR(MIN_MONTH) _S(" and ") NUM2STR(MAX_MONTH), -1);

   switch (month) {
      case 1:
      case 3:
      case 5:
      case 7:
      case 8:
      case 10:
      case 12:
         return 31;
      case 4:
      case 6:
      case 9:
      case 11:
         return 30;
      case 2:
         return (DateTime::IsLeapYear(year)) ? 29 : 28;
   }

   return 0;
}

/**
 *  Calculate day week by a given date
 */
cppflib::util::dayofweek_t DateTime::DayOfWeek(int year, int month, int day)
{
   __CheckDatePart(year, month, day);

   int orgDay = day, orgMonth = month, orgYear = year;  
   int monthCon, century, yearPart, yearCon;
   int result = 0;


   // step 1 -- adjust month code and year code
   if (month >= 3 && month <= 12) {
      month -= 2;
   }
   else  {
      year -= 1;
      month += 10;
   }
    
   // step 2 month contribution whole part of 26 x month - 2 divided by 10  
   monthCon = (26 * month - 2) / 10;

   // step 3 -- year contribution add 
   //           (i) the year within the century
   //           (ii) the whole number part of the result of dividing the year
   //           (iii) the whole number part of the result of dividing the 
   //                 century by 4
   //           (iv) five times the century

   century = year / 100;
   yearPart = year % 100;
   yearCon = yearPart + (yearPart / 4) + (century / 4) + (century * 5);

   // step 4 -- add day, month contribution and year contribution. Divide by 7
   //           to get a remainder which is used to determine the week.
   
   result = (day + monthCon + yearCon) % 7;   
   return static_cast<cppflib::util::dayofweek_t>(result);
}

/**
 *  Get an instance representing current local time
 */
DateTime * DateTime::Now()
{
   return new DateTime(__GetNow());
}

/**
 *  Get an instance representing current UTC time
 */
DateTime * DateTime::UtcNow()
{
   return new DateTime(__GetUtcNow());
}

/**
 *  Get today's date, no time part
 *
 *  @return a local date
 */
DateTime * DateTime::Today()
{
   DateTime localDateTime = __GetNow();
   return new DateTime(localDateTime.Year(), localDateTime.Month(), localDateTime.Day(), localDateTime.Kind());
}

/**
 *  Same as Now() but return a value on stack
 */
DateTime DateTime::NowValue()
{
   return DateTime(__GetNow());
}

/**
 *  Same as UtcNow() but return a value on stack
 */
DateTime DateTime::UtcNowValue()
{
   return DateTime(__GetUtcNow());
}

/**
 *  Get minimum datetime value
 */
DateTime DateTime::MinDateTime()
{
   return DateTime(MIN_DATETIMEVALUE, DateTime::DTK_UTC);
}

/**
 *  Get maximum datetime value
 */
DateTime DateTime::MaxDateTime()
{
   return DateTime(MAX_DATETIMEVALUE, DateTime::DTK_UTC);
}

/**
 *  Convert a unix epoch time to this DateTime
 *
 *  @param [in] epochTime -- number of seconds since 1970, 1, 1 UTC
 *                           (can be +ve or -ve)
 */
DateTime DateTime::GetDateTimeFromEpoch(i64_t epochTime)
{
   DateTime dt(1970, 1, 1, DateTime::DTK_UTC);
   dt.AddSeconds(epochTime);
   return dt;
}

/**
 *  Convert a DateTime to unix epoch time
 *
 *  @param [in] dt -- an instance of date time
 *
 *  @return number of seconds since 1970, 1, 1 UTC (can be +ve or -ve)
 */
i64_t DateTime::GetEpochFromDateTime(const DateTime& dt)
{
   DateTime dtUtc = dt.ToUtcTime();
   DateTime epochStart(1970, 1, 1, DateTime::DTK_UTC);
   TimeDuration td = dtUtc - epochStart;
   return td.IsNeg() ? -td.TotalSeconds() : td.TotalSeconds();
}


_DLLAPI bool operator==(const DateTime& d1, const DateTime& d2)
{
   return d1.Compare(d2) == 0;
}

_DLLAPI bool operator!=(const DateTime& d1, const DateTime& d2)
{
   return d1.Compare(d2) != 0;
}

_DLLAPI bool operator<(const DateTime& d1, const DateTime& d2)
{
   return d1.Compare(d2) < 0;
}

_DLLAPI bool operator>(const DateTime& d1, const DateTime& d2)
{
   return d1.Compare(d2) > 0;
}

_DLLAPI bool operator<=(const DateTime& d1, const DateTime& d2)
{
   return d1.Compare(d2) <= 0;
}

_DLLAPI bool operator>=(const DateTime& d1, const DateTime& d2)
{
   return d1.Compare(d2) >= 0;
}

_DLLAPI DateTime operator+(const DateTime& dt, const TimeDuration& td)
{
   DateTime temp(dt);
   temp.Add(td);
   return temp;
}

_DLLAPI DateTime operator-(const DateTime& dt, const TimeDuration& td)
{
   DateTime temp(dt);
   temp.AddTicks(-td.TotalTicks());
   return temp;
}

_DLLAPI TimeDuration operator-(const DateTime& d1, const DateTime& d2)
{
   return TimeDuration(d1.ToUtcTime().Ticks() - d2.ToUtcTime().Ticks());
}


} // end of namespace util

} // end of namespace cppflib
